﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using Common;

namespace eBay {
    public class Categories {
        /// <summary>
        /// Get the Name of a category from the XMLDocument
        /// </summary>
        /// <param name="CategoryID">The ID of the category to get the name for</param>
        /// <returns>Name of the Category</returns>
        public static string GetCategoryName(int CategoryID) {
            //go through each category
            foreach (XmlNode cat in Global.CategoriesTree["GetCategoriesResponse"]["CategoryArray"].ChildNodes) {
                //if this cat is the one, then return its CategoryName value
                if (cat.Name == "Category" && cat["CategoryID"].InnerText == CategoryID.ToString()) {
                    return cat["CategoryName"].InnerText;
                }
            }

            return null; //not found
        }
        /// <summary>
        /// Whether or not the specified category is a Leaf category
        /// (so it can be submitted to)
        /// </summary>
        /// <param name="CategoryID">The ID of the category to check</param>
        /// <returns>True if it is a Leaf category, false otherwise</returns>
        private static bool IsCategoryLeaf(int CategoryID) {
            // go through each category
            foreach (XmlNode cat in Global.CategoriesTree["GetCategoriesResponse"]["CategoryArray"].ChildNodes) {
                //if this cat is the one, then return its LeafCategory value
                if (cat.Name == "Category" && cat["CategoryID"].InnerText == CategoryID.ToString()) {
                    if (cat["LeafCategory"].InnerText == "1" || cat["LeafCategory"].InnerText == "true")
                        return true;
                    else
                        return false;
                }
            }

            return true; //not found
        }
        /// <summary>
        /// Requests the entire category tree from the eBay API and saves it locally.
        /// </summary>
        /// <returns>True if successful, false otherwise.</returns>
        public static bool GetEntireCategoryTree() {
            #region Load The XML Document Template and Set the Neccessary Values
            //Load the XML Document to Use for this Request
            XmlDocument xmlDoc = new XmlDocument();

            //Get XML Document from Embedded  Resources
            xmlDoc.Load(Common.Funcs.GetAppPath() + "APICalls\\GetCategoriesRequest.xml");

            //Set the various node values
            xmlDoc["GetCategoriesRequest"]["RequesterCredentials"]["eBayAuthToken"].InnerText = Global.Configuration.UserToken;
            //Tell API to return entire tree
            xmlDoc["GetCategoriesRequest"]["DetailLevel"].InnerText = "ReturnAll";
            xmlDoc["GetCategoriesRequest"]["ViewAllNodes"].InnerText = "1";

            //Get XML into a string for use in encoding
            string xmlText = xmlDoc.InnerXml;

            //Put the data into a UTF8 encoded  byte array
            UTF8Encoding encoding = new UTF8Encoding();
            int dataLen = encoding.GetByteCount(xmlText);
            byte[] utf8Bytes = new byte[dataLen];
            Encoding.UTF8.GetBytes(xmlText, 0, xmlText.Length, utf8Bytes, 0);
            #endregion

            #region Setup and Send The Request
            HttpWebRequest request = eBayRequest.SetupRequest("GetCategories", utf8Bytes.Length, 551);
            //GetHttpRequestWithHeaders(utf8Bytes.Length);


            Stream str = null;
            try {
                //Set the request Stream
                str = request.GetRequestStream();
                //Write the equest to the Request Steam
                str.Write(utf8Bytes, 0, utf8Bytes.Length);
                str.Close();

                //Get response into stream
                WebResponse resp = request.GetResponse();
                str = resp.GetResponseStream();
            }
            catch (WebException wEx) {
                //Error has occured whilst requesting
                //Display error message and exit.
                if (wEx.Status == WebExceptionStatus.Timeout)
                    Console.WriteLine("Request Timed-Out.");
                else
                    Console.WriteLine(wEx.Message);
                return false;
            }
            #endregion

            #region Process Response
            // Get Response into String
            StreamReader sr = new StreamReader(str);
            xmlDoc.LoadXml(sr.ReadToEnd());

            sr.Close();
            str.Close();

            //get the root node, for ease of use
            XmlNode root = xmlDoc["GetCategoriesResponse"];

            //There have been Errors
            if (root["Errors"] != null) {
                string errorCode = root["Errors"]["ErrorCode"].InnerText;
                string errorShort = root["Errors"]["ShortMessage"].InnerText;
                string errorLong = root["Errors"]["LongMessage"].InnerText;

                //Output the error message
                Console.WriteLine(errorCode + " ERROR: " + errorShort);
                Console.WriteLine(errorLong + "\n");

                return false;
            }
            else {
                //Save the file to disk
                StreamWriter sw = new StreamWriter(Common.Funcs.GetAppPath() + "CatTree.xml", false, Encoding.UTF8);
                xmlDoc.Save(sw);
                sw.Close();
            }

            return true;
            #endregion
        }
        /// <summary>
        /// Requests the current version number of the Category Tree that
        /// is available online.
        /// </summary>
        /// <returns>Version number as string, null if there is an error</returns>
        public static string GetCurrentAPICategoryVersion() {
            #region Load The XML Document Template and Set the Neccessary Values
            //Load the XML Document to Use for this Request
            XmlDocument xmlDoc = new XmlDocument();

            //Get XML Document from Embedded Resources
            xmlDoc.Load(Common.Funcs.GetAppPath() + "APICalls\\GetCategoriesRequest.xml");

            //Set the various node values
            xmlDoc["GetCategoriesRequest"]["RequesterCredentials"]["eBayAuthToken"].InnerText = Global.Configuration.UserToken;
            //Tell API to return just the category version number (remove DetailLevel node)
            xmlDoc["GetCategoriesRequest"].RemoveChild(xmlDoc["GetCategoriesRequest"]["DetailLevel"]);
            xmlDoc["GetCategoriesRequest"]["ViewAllNodes"].InnerText = "0";

            //Get XML into a string for use in encoding
            string xmlText = xmlDoc.InnerXml;

            //Put the data into a UTF8 encoded  byte array
            UTF8Encoding encoding = new UTF8Encoding();
            int dataLen = encoding.GetByteCount(xmlText);
            byte[] utf8Bytes = new byte[dataLen];
            Encoding.UTF8.GetBytes(xmlText, 0, xmlText.Length, utf8Bytes, 0);
            #endregion

            #region Setup and Send The Request
            HttpWebRequest request = eBayRequest.SetupRequest("GetCategories", utf8Bytes.Length, 551);

            Stream str = null;
            try {
                //Set the request Stream
                str = request.GetRequestStream();
                //Write the request to the Request Steam
                str.Write(utf8Bytes, 0, utf8Bytes.Length);
                str.Close();
                //Get response into stream
                WebResponse resp = request.GetResponse();
                str = resp.GetResponseStream();
            }
            catch (WebException wEx) {
                //Error has occured whilst requesting
                //Display error message and exit.
                if (wEx.Status == WebExceptionStatus.Timeout)
                    Console.WriteLine("Request Timed-Out.");
                else
                    Console.WriteLine(wEx.Message);
                return null;
            }
            #endregion

            #region Process Response
            // Get Response into String
            StreamReader sr = new StreamReader(str);
            xmlDoc.LoadXml(sr.ReadToEnd());

            sr.Close();
            str.Close();

            //get the root node, for ease of use
            XmlNode root = xmlDoc["GetCategoriesResponse"];

            //There have been Errors
            if (root["Errors"] != null) {
                string errorCode = root["Errors"]["ErrorCode"].InnerText;
                string errorShort = root["Errors"]["ShortMessage"].InnerText;
                string errorLong = root["Errors"]["LongMessage"].InnerText;

                //Output the error message
                Console.WriteLine(errorCode + " ERROR: " + errorShort);
                Console.WriteLine(errorLong + "\n");

                return null;
            }
            else {
                //return the version number
                return root["CategoryVersion"].InnerText;
            }
            #endregion
        }
        /// <summary>
        /// Returns a HTTP Request object with all the neccessary headers set.
        /// </summary>
        /// <param name="dataLength">Length of the XML data being sent</param>
        /// <param name="detailLevel">Detail Level Required</param>
        /// <returns></returns>
        public static HttpWebRequest GetHttpRequestWithHeaders(long dataLength) {
            //Create a new HttpWebRequest object for the ServerUrl
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Global.Configuration.ServerUrl);

            //Set Request Method (POST) and Content Type (text/xml)
            request.Method = "POST";
            request.ContentType = "text/xml";
            request.ContentLength = dataLength;

            //Add the Keys to the HTTP Headers
            request.Headers.Add("X-EBAY-API-DEV-NAME: " + Global.Configuration.DevID);
            request.Headers.Add("X-EBAY-API-APP-NAME: " + Global.Configuration.AppIDSandbox);
            request.Headers.Add("X-EBAY-API-CERT-NAME: " + Global.Configuration.CertID);

            //Add Compatability Level to HTTP Headers
            //Regulates versioning of the XML interface for the API
            request.Headers.Add("X-EBAY-API-COMPATIBILITY-LEVEL: 551");

            //Add function name, SiteID and Detail Level to HTTP Headers
            request.Headers.Add("X-EBAY-API-CALL-NAME: GetCategories");
            request.Headers.Add("X-EBAY-API-SITEID: " + Global.Configuration.SiteID);

            //Time out = 15 seconds,  set to -1 for no timeout.
            //If times-out - throws a WebException with the
            //Status property set to WebExceptionStatus.Timeout.
            request.Timeout = 15000;

            return request;
        }

        /// <summary>
        /// Gets the latest version of Categories Dictionary 
        /// from the eBay site
        /// </summary>
        /// <returns>XmlDocument representing the Categories Dictionary</returns>
        /// <exception cref="eBay.API.Categories.CouldNotObtainCategoryTree">Could not obtain category tree from online</exception>
        /// <exception cref="eBay.API.Categories.CouldNotEstablishlatestVersionNumber">Could not establish latest version number</exception>
        public static XmlDocument GetCategories() {
            XmlDocument localCatTree = null;
            //If local version cannot be found: get and save entire category tree
            if (!File.Exists(Common.Funcs.GetAppPath() + "CatTree.xml")) {
                //Application Run for first time, downloading entire category tree...
                if (!Categories.GetEntireCategoryTree())
                    throw new Categories.CouldNotObtainCategoryTree();
            }
            else //Xml file already exists locally
            {
                //Open Xml File
                localCatTree = new XmlDocument();
                localCatTree.Load(Common.Funcs.GetAppPath() + "CatTree.xml");

                //Get the latest version number
                string onlineVersion = Categories.GetCurrentAPICategoryVersion();

                if (onlineVersion == null)
                    throw new Categories.CouldNotEstablishLatestVersionNumber();


                if (localCatTree["GetCategoriesResponse"]["CategoryVersion"].InnerText != onlineVersion) {
                    //Version numbers are different, so update
                    if (!Categories.GetEntireCategoryTree())
                        throw new Categories.CouldNotObtainCategoryTree();
                }

            }
            XmlDocument result = new XmlDocument();
            result.Load(Common.Funcs.GetAppPath() + "CatTree.xml");
            return result;
        }

        public class CouldNotObtainCategoryTree : Exception { }
        public class CouldNotEstablishLatestVersionNumber : Exception { }
    }
}